package com.dream.myqiyi.widget;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Typeface;
import android.text.Editable;
import android.text.Layout;
import android.text.TextWatcher;
import android.text.style.BackgroundColorSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
import android.text.style.UnderlineSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.EditText;

public class RichEditText extends EditText implements EditorActionModeListener {
	public static final int FMT_BOLD = 0;
	public static final int FMT_ITALIC = 1;
	public static final int FMT_UNDERLINE = 2;
	public static final int FMT_STRIKETHROUGH = 3;
	public static final int FMT_TYPEFACE = 4;
	public static final int FMT_SUPERSCRIPT = 5;
	public static final int FMT_SUBSCRIPT = 6;
	public static final int FMT_TYPEFACE_SERIF = 7;
	public static final int FMT_TYPEFACE_SANS = 8;
	public static final int FMT_TYPEFACE_MONO = 9;
	public static final int FMT_ALIGN_NORMAL = 10;
	public static final int FMT_ALIGN_CENTER = 11;
	public static final int FMT_ALIGN_OPPOSITE = 12;
	public static final int FMT_BACKGROUNDCOLOR = 13;
	
	public static final Effect<Boolean> BOLD = new StyleEffect(Typeface.BOLD);
	public static final Effect<Boolean> ITALIC = new StyleEffect(Typeface.ITALIC);
	public static final Effect<Boolean> UNDERLINE = new UnderlineEffect();
	public static final Effect<Boolean> STRIKETHROUGH = new StrikethroughEffect();
	public static final Effect<Boolean> BACKGROUNDCOLOR = new BackgroundColorEffect();
	public static final Effect<Boolean> SUPERSCRIPT = new SuperscriptEffect();
	public static final Effect<Boolean> SUBSCRIPT=new SubscriptEffect();
	public static final Effect<Layout.Alignment> LINE_ALIGNMENT = new LineAlignmentEffect();
	public static final Effect<String> TYPEFACE = new TypefaceEffect();

	private static final ArrayList<Effect<?>> EFFECTS = new ArrayList<Effect<?>>();
	private boolean isSelectionChanging = false;
	private OnSelectionChangedListener selectionListener=null;

	//private boolean actionModeIsShowing=false;
	//private EditorActionModeCallback.Native mainMode=null;
	//private EditorActionModeCallback.ABS sherlockEntryMode=null;
	//private boolean forceActionMode=false;
	//private com.actionbarsherlock.view.ActionMode sherlockActionMode=null;
	//private boolean keyboardShortcuts=true;

	/*
	 * EFFECTS is a roster of all defined effects, for simpler
	 * iteration over all the possibilities.
	 */
	static {
	    /*
	     * Boolean effects
	     */
		EFFECTS.add(BOLD);
	    EFFECTS.add(ITALIC);
	    EFFECTS.add(UNDERLINE);
	    EFFECTS.add(STRIKETHROUGH);
	    EFFECTS.add(SUPERSCRIPT);
	    EFFECTS.add(SUBSCRIPT);
	    EFFECTS.add(BACKGROUNDCOLOR);

	    /*
	     * Non-Boolean effects
	     */
	    EFFECTS.add(LINE_ALIGNMENT);
	    EFFECTS.add(TYPEFACE);
	}

	/*
	 * Standard one-parameter widget constructor, simply
	 * chaining to superclass.
	 */
	public RichEditText(Context context) {
		super(context);
		addTextChangedListener(textWatcher);
	}

	/*
	 * Standard two-parameter widget constructor, simply
	 * chaining to superclass.
	 */
	public RichEditText(Context context, AttributeSet attrs) {
		super(context, attrs);
		addTextChangedListener(textWatcher);
	}

	/*
	 * Standard three-parameter widget constructor, simply
	 * chaining to superclass.
	 */
	public RichEditText(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		addTextChangedListener(textWatcher);
	}

	/*
	 * If there is a registered OnSelectionChangedListener,
	 * checks to see if there are any effects applied to the
	 * current selection, and supplies that information to the
	 * registrant.
	 * 
	 * Uses isSelectionChanging to avoid updating anything
	 * while this callback is in progress (e.g., registrant
	 * updates a ToggleButton, causing its
	 * OnCheckedChangeListener to fire, causing it to try to
	 * update the RichEditText as if the user had clicked upon
	 * it.
	 * 
	 * @see android.widget.TextView#onSelectionChanged(int,
	 * int)
	 */
	@Override
	public void onSelectionChanged(int start, int end) {
		super.onSelectionChanged(start, end);

	    if (selectionListener != null) {
	    	ArrayList<Effect<?>> effects=new ArrayList<Effect<?>>();

	    	for (Effect<?> effect : EFFECTS) {
	    		if (effect.existsInSelection(this)) {
	    			effects.add(effect);
	    		}
	    	}

	    	isSelectionChanging=true;
	    	selectionListener.onSelectionChanged(start, end, effects);
	    	isSelectionChanging=false;
	    }
	}

	/*
	 * Call this to provide a listener object to be notified
	 * when the selection changes and what the applied effects
	 * are for the current selection. Designed to be used by a
	 * hosting activity to adjust states of toolbar widgets
	 * (e.g., check/uncheck a ToggleButton).
	 */
	public void setOnSelectionChangedListener(OnSelectionChangedListener selectionListener) {
		this.selectionListener=selectionListener;
	}

	/*
	 * Call this to enable or disable handling of keyboard
	 * shortcuts (e.g., Ctrl-B for bold). Enabled by default.
	 */
//	public void setKeyboardShortcutsEnabled(boolean keyboardShortcuts) {
//		this.keyboardShortcuts=keyboardShortcuts;
//	}

	/*
	 * Call this to have an effect applied to the current
	 * selection. You get the Effect object via the static
	 * data members (e.g., RichEditText.BOLD). The value for
	 * most effects is a Boolean, indicating whether to add or
	 * remove the effect.
	 */
	public <T> void applyEffect(Effect<T> effect, T value) {
		if (!isSelectionChanging) {
			effect.applyToSelection(this, value);
		}
	}

	/*
	 * Returns true if a given effect is applied somewhere in
	 * the current selection. This includes the effect being
	 * applied in a subset of the current selection.
	 */
	public boolean hasEffect(Effect<?> effect) {
		return(effect.existsInSelection(this));
	}

	/*
	 * Returns the value of the effect applied to the current
	 * selection. For Effect<Boolean> (e.g.,
	 * RichEditText.BOLD), returns the same value as
	 * hasEffect(). Otherwise, returns the highest possible
	 * value, if multiple occurrences of this effect are
	 * applied to the current selection. Returns null if there
	 * is no such effect applied.
	 */
	public <T> T getEffectValue(Effect<T> effect) {
		return(effect.valueInSelection(this));
	}

	/*
	 * If the effect is presently applied to the current
	 * selection, removes it; if the effect is not presently
	 * applied to the current selection, adds it.
	 */
	public void toggleEffect(Effect<Boolean> effect) {
		if (!isSelectionChanging) {
			effect.applyToSelection(this, !effect.valueInSelection(this));
		}
	}

	@Override
	public boolean doAction(int itemId) {
		if (itemId == FMT_UNDERLINE) {
			toggleEffect(RichEditText.UNDERLINE);
			return(true);
	    } else if (itemId == FMT_STRIKETHROUGH) {
	    	toggleEffect(RichEditText.STRIKETHROUGH);
	    	return(true);
	    } else if (itemId == FMT_SUPERSCRIPT) {
	    	toggleEffect(RichEditText.SUPERSCRIPT);
	    	return(true);
	    } else if (itemId == FMT_SUBSCRIPT) {
	    	toggleEffect(RichEditText.SUBSCRIPT);
	    	return(true);
	    } else if (itemId == FMT_BACKGROUNDCOLOR) {
	    	toggleEffect(RichEditText.BACKGROUNDCOLOR);
	    	return(true);
	    } else if (itemId == FMT_TYPEFACE_SERIF) {
	    	applyEffect(RichEditText.TYPEFACE, "serif");
	    	return(true);
	    } else if (itemId == FMT_TYPEFACE_SANS) {
	    	applyEffect(RichEditText.TYPEFACE, "sans");
	    	return(true);
	    } else if (itemId == FMT_TYPEFACE_MONO) {
	    	applyEffect(RichEditText.TYPEFACE, "monospace");
	    	return(true);
	    } else if (itemId == FMT_ALIGN_NORMAL) {
	    	applyEffect(RichEditText.LINE_ALIGNMENT, Layout.Alignment.ALIGN_NORMAL);
	    	return(true);
	    } else if (itemId == FMT_ALIGN_CENTER) {
	    	applyEffect(RichEditText.LINE_ALIGNMENT, Layout.Alignment.ALIGN_CENTER);
	    	return(true);
	    } else if (itemId == FMT_ALIGN_OPPOSITE) {
	    	applyEffect(RichEditText.LINE_ALIGNMENT, Layout.Alignment.ALIGN_OPPOSITE);
	    	return(true);
	    } else if (itemId == FMT_BOLD) {
	    	toggleEffect(RichEditText.BOLD);
	    	return(true);
	    } else if (itemId == FMT_ITALIC) {
	    	toggleEffect(RichEditText.ITALIC);
	    	return(true);
	    } else if (itemId == android.R.id.selectAll || 
	    		   itemId == android.R.id.cut || 
	    		   itemId == android.R.id.copy || 
	    		   itemId == android.R.id.paste) {
	    	onTextContextMenuItem(itemId);
	    }
	    return(false);
	}

	@Override
	public void setIsShowing(boolean isShowing) {
		//actionModeIsShowing=isShowing;
	}

	public void enableActionModes(boolean forceActionMode) {
		//this.forceActionMode=forceActionMode;

//	    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
//	    	enableNativeActionModes();
//	    } else {
//	    	enableSherlockActionModes();
//	    }
	}

	public void disableActionModes() {
//		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
//			setCustomSelectionActionModeCallback(null);
//			mainMode=null;
//	    } else {
//	    	sherlockEntryMode=null;
//	    }
	}
	
	private TextWatcher textWatcher = new TextWatcher() {
		ArrayList<Effect<?>> effects = new ArrayList<Effect<?>>();
		@Override
		public void afterTextChanged(Editable s) {
			// TODO Auto-generated method stub
			Log.d("afterTextChanged", s.toString());
		}

		@Override
		public void beforeTextChanged(CharSequence s, int start, int count, int after) {
			// TODO Auto-generated method stub
			Log.d("beforeTextChanged", s.toString());
			Log.d("beforeTextChanged", Integer.toString(start));
			Log.d("beforeTextChanged", Integer.toString(count));
			Log.d("beforeTextChanged", Integer.toString(after));
			
			effects.clear();
			
	    	for (Effect<?> effect : EFFECTS) {
	    		if (effect.existsInSelection(RichEditText.this)) {
	    			effects.add(effect);
	    		}
	    	}

		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before, int count) {
			// TODO Auto-generated method stub
	    	for (Effect<?> effect : effects) {
	    		if (effect == BOLD) {
	    			BOLD.applyToSelection(RichEditText.this, BOLD.valueInSelection(RichEditText.this));
	    		} else if (effect == ITALIC) {
	    			ITALIC.applyToSelection(RichEditText.this, ITALIC.valueInSelection(RichEditText.this));
	    		} else if (effect == UNDERLINE) {
	    			UNDERLINE.applyToSelection(RichEditText.this, UNDERLINE.valueInSelection(RichEditText.this));
	    		} else if (effect == STRIKETHROUGH) {
	    			STRIKETHROUGH.applyToSelection(RichEditText.this, STRIKETHROUGH.valueInSelection(RichEditText.this));
	    		} else if (effect == BACKGROUNDCOLOR) {
	    			BACKGROUNDCOLOR.applyToSelection(RichEditText.this, BACKGROUNDCOLOR.valueInSelection(RichEditText.this));
	    		} else if (effect == SUPERSCRIPT) {
	    			SUPERSCRIPT.applyToSelection(RichEditText.this, SUPERSCRIPT.valueInSelection(RichEditText.this));
	    		} else if (effect == SUBSCRIPT) {
	    			SUBSCRIPT.applyToSelection(RichEditText.this, SUBSCRIPT.valueInSelection(RichEditText.this));
	    		}
	    	}			
		}
		
	};

//	private void enableNativeActionModes() {
//		EditorActionModeCallback.Native effectsMode=
//	        new EditorActionModeCallback.Native(
//	                                            (Activity)getContext(),
//	                                            R.menu.cwac_richedittext_effects,
//	                                            this, this);
//
//	    EditorActionModeCallback.Native fontsMode=
//	        new EditorActionModeCallback.Native(
//	                                            (Activity)getContext(),
//	                                            R.menu.cwac_richedittext_fonts,
//	                                            this, this);
//
//	    mainMode=
//	        new EditorActionModeCallback.Native(
//	                                            (Activity)getContext(),
//	                                            R.menu.cwac_richedittext_main,
//	                                            this, this);
//
//	    mainMode.addChain(R.id.cwac_richedittext_effects, effectsMode);
//	    mainMode.addChain(R.id.cwac_richedittext_fonts, fontsMode);
//
//	    EditorActionModeCallback.Native entryMode=
//	        new EditorActionModeCallback.Native(
//	                                            (Activity)getContext(),
//	                                            R.menu.cwac_richedittext_entry,
//	                                            this, this);
//
//	    entryMode.addChain(R.id.cwac_richedittext_format, mainMode);
//
//	    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
//	    	setCustomSelectionActionModeCallback(entryMode);
//	    }
//	}

//	private void enableSherlockActionModes() {
//	    EditorActionModeCallback effectsMode=
//	        new EditorActionModeCallback.ABS(
//	                                         (Activity)getContext(),
//	                                         R.menu.cwac_richedittext_effects,
//	                                         this, this);
//
//	    EditorActionModeCallback fontsMode=
//	        new EditorActionModeCallback.ABS(
//	                                         (Activity)getContext(),
//	                                         R.menu.cwac_richedittext_fonts,
//	                                         this, this);
//
//	    EditorActionModeCallback sherlockMainMode=
//	        new EditorActionModeCallback.ABS((Activity)getContext(),
//	                                         R.menu.cwac_richedittext_main,
//	                                         this, this);
//
//	    sherlockMainMode.addChain(R.id.cwac_richedittext_effects,
//	                              effectsMode);
//	    sherlockMainMode.addChain(R.id.cwac_richedittext_fonts, fontsMode);
//
//	    sherlockEntryMode=
//	        new EditorActionModeCallback.ABS(
//	                                         (Activity)getContext(),
//	                                         R.menu.cwac_richedittext_entry,
//	                                         this, this);
//
//	    sherlockEntryMode.addChain(R.id.cwac_richedittext_format,
//	                               sherlockMainMode);
//	}

//	private void showSherlockEntryMode() {
//	    if (!actionModeIsShowing) {
//	    	// nasty reflection hack to get around the fact
//	    	// that there is no inheritance hierarchy for
//	    	// Sherlock*Activity
//
//	    	Method method;
//	    	try {
//	    		method= getContext().getClass().getMethod("startActionMode",
//	                                   com.actionbarsherlock.view.ActionMode.Callback.class);
//	    		sherlockActionMode = (com.actionbarsherlock.view.ActionMode)method.invoke(getContext(),
//	                                                                 sherlockEntryMode);
//	    	} catch (Exception e) {
//	    		Log.e(getClass().getSimpleName(), "Exception starting action mode", e);
//	    	}
//	    }
//	}

	/*
	 * Interface for listener object to be registered by
	 * setOnSelectionChangedListener().
	 */
	public interface OnSelectionChangedListener {
	    /*
	     * Provides details of the new selection, including the
	     * start and ending character positions, and a roster of
	     * all effects presently applied (so you can bulk-update
	     * a toolbar when the selection changes).
	     */
		void onSelectionChanged(int start, int end, List<Effect<?>> effects);
	}

	private static class UnderlineEffect extends SimpleBooleanEffect<UnderlineSpan> {
		UnderlineEffect() {
			super(UnderlineSpan.class);
		}
	}

	private static class StrikethroughEffect extends SimpleBooleanEffect<StrikethroughSpan> {
		StrikethroughEffect() {
			super(StrikethroughSpan.class);
	    }
	}

	private static class BackgroundColorEffect extends SimpleBooleanEffect<BackgroundColorSpan> {
		BackgroundColorEffect() {
			super(BackgroundColorSpan.class);
	    }
	}
	
	private static class SuperscriptEffect extends SimpleBooleanEffect<SuperscriptSpan> {
		SuperscriptEffect() {
			super(SuperscriptSpan.class);
	    }
	}

	private static class SubscriptEffect extends SimpleBooleanEffect<SubscriptSpan> {
		SubscriptEffect() {
			super(SubscriptSpan.class);
	    }
	}

}
